package com.yanqu.road.server.gameplayer.module.activity.seacraft;

import com.alibaba.fastjson.JSON;
import com.yanqu.road.entity.activity.args.SeacraftPirateRiotScoreRankArgs;
import com.yanqu.road.entity.activity.seacraft.pirateriot.SeacraftPirateRiotBattlePatrons;
import com.yanqu.road.entity.activity.seacraft.pirateriot.SeacraftPirateRiotBuff;
import com.yanqu.road.entity.activity.seacraft.pirateriot.SeacraftPirateRiotChange;
import com.yanqu.road.entity.activity.seacraft.pirateriot.SeacraftPirateRiotMonsterData;
import com.yanqu.road.entity.activity.seacraft.pirateriot.SeacraftPirateRiotCheckChapterResult;
import com.yanqu.road.entity.activity.seacraft.pirateriot.SeacraftPirateRiotUserData;
import com.yanqu.road.entity.activity.seacraft.pirateriot.config.SeacraftPirateRiotChapterConfig;
import com.yanqu.road.entity.activity.seacraft.pirateriot.config.SeacraftPirateRiotConfig;
import com.yanqu.road.entity.activity.seacraft.pirateriot.config.SeacraftPirateRiotEquipConfig;
import com.yanqu.road.entity.activity.seacraft.pirateriot.config.SeacraftPirateRiotMonsterConfig;
import com.yanqu.road.entity.activity.seacraft.pirateriot.config.SeacraftPirateRiotSectionConfig;
import com.yanqu.road.entity.activity.seacraft.pirateriot.config.SeacraftPirateRiotSkillConfig;
import com.yanqu.road.entity.activity.seacraft.pirateriot.eliminate.SeacraftPirateRiotBlock;
import com.yanqu.road.entity.activity.seacraft.pirateriot.eliminate.SeacraftPirateRiotBoom;
import com.yanqu.road.entity.activity.seacraft.pirateriot.eliminate.SeacraftPirateRiotEliminateResult;
import com.yanqu.road.entity.activity.seacraft.pirateriot.eliminate.SeacraftPirateRiotEliminateRoundResult;
import com.yanqu.road.entity.activity.seacraft.pirateriot.eliminate.SeacraftPirateRiotEliminateStageResult;
import com.yanqu.road.entity.activity.seacraft.pirateriot.eliminate.SeacraftPirateRiotEliminateTips;
import com.yanqu.road.entity.config.goods.GoodsInfo;
import com.yanqu.road.entity.config.patrons.PatronsInfo;
import com.yanqu.road.entity.enums.eGamePlayerEventType;
import com.yanqu.road.entity.enums.eLogMoneyType;
import com.yanqu.road.entity.enums.ePirateRiotChapterStatus;
import com.yanqu.road.entity.enums.ePlayerAttrType;
import com.yanqu.road.entity.enums.eSkillType;
import com.yanqu.road.entity.enums.eSystemId;
import com.yanqu.road.entity.log.LogSeacraftPirateRiotChapterReward;
import com.yanqu.road.entity.log.LogSeacraftPirateRiotEliminate;
import com.yanqu.road.entity.log.LogSeacraftPirateRiotEnergy;
import com.yanqu.road.entity.log.LogSeacraftPirateRiotEquipUpgrade;
import com.yanqu.road.entity.player.UserPatrons;
import com.yanqu.road.entity.task.args.UnionRiseRankArgs;
import com.yanqu.road.logic.bussiness.activity.SeacraftPirateRiotBussiness;
import com.yanqu.road.logic.helper.CalcPowerResult;
import com.yanqu.road.logic.helper.PowerHelper;
import com.yanqu.road.logic.sort.SeacraftPirateRiotMonsterAttackSort;
import com.yanqu.road.pb.activity.SeacraftPirateRiotProto;
import com.yanqu.road.server.gameplayer.GamePlayer;
import com.yanqu.road.server.gameplayer.module.base.GeneralModule;
import com.yanqu.road.server.gameplayer.module.patrons.PatronsModule;
import com.yanqu.road.server.gameplayer.module.player.AttributeModule;
import com.yanqu.road.server.gameplayer.module.player.CurrencyModule;
import com.yanqu.road.server.manger.activity.ActivityMgr;
import com.yanqu.road.server.manger.activity.curio.CurioMgr;
import com.yanqu.road.server.manger.activity.seacraft.SeacraftPirateRiotEliminateLogic;
import com.yanqu.road.server.manger.activity.seacraft.SeacraftPirateRiotMgr;
import com.yanqu.road.server.manger.config.GoodsMgr;
import com.yanqu.road.server.manger.config.PatronsMgr;
import com.yanqu.road.server.manger.config.SystemOpenMgr;
import com.yanqu.road.server.manger.log.AutoLogMgr;
import com.yanqu.road.server.manger.log.LogMgr;
import com.yanqu.road.server.pb.SeacraftPirateRiotPb;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

public class SeacraftPirateRiotModule extends GeneralModule {

    private RandomHelper randomHelper = new RandomHelper();

    private SeacraftPirateRiotUserData userData;

    public SeacraftPirateRiotModule(GamePlayer player) {
        super(player);
    }

    @Override
    public boolean loadData() {

        // 初始化最大关卡ID
        if (!player.getModule(AttributeModule.class).getUserAttributeMap().containsKey(ePlayerAttrType.SeacraftPirateRiotMaxChapterId)) {
            int maxChapterId = 0;

            // 找最大关卡ID
            List<List<Integer>> list = SeacraftPirateRiotBussiness.getAllActivityChapter(getUserId());
            List<Integer> chapterIdList = list.get(0);
            List<Integer> chapterStatusList = list.get(1);
            for (int i = 0; i < chapterIdList.size(); i++) {
                int chapterId = chapterIdList.get(i);
                int chapterStatus = chapterStatusList.get(i);
                if (chapterStatus != ePirateRiotChapterStatus.FINISH.getValue() && chapterStatus != ePirateRiotChapterStatus.WAIT_REWARD.getValue()) {
                    chapterId--; // 不是通关状态的话，减1关
                }

                maxChapterId = Math.max(maxChapterId, chapterId);
            }

            // 设置最大关卡ID
            player.getModule(AttributeModule.class).setAttribute(ePlayerAttrType.SeacraftPirateRiotMaxChapterId, maxChapterId);
            log.info("userId {} set max chapterId {}.", getUserId(), maxChapterId);
        }

        // 读库
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (config != null && ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            userData = SeacraftPirateRiotBussiness.getSeacraftPirateRiotUserData(config.getActivityInfo().getActivityId(), getUserId());
        }

        return true;
    }

    @Override
    public boolean afterLoadData() {
        return true;
    }

    @Override
    public boolean saveData() {
        if (userData != null) {
            if (userData.isInsertOption()) {
                SeacraftPirateRiotBussiness.addSeacraftPirateRiotUserData(userData);
            } else if (userData.isUpdateOption()) {
                SeacraftPirateRiotBussiness.updateSeacraftPirateRiotUserData(userData);
            }
        }
        return true;
    }

    @Override
    public void afterLogin() {
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (config != null && ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            initUserData(config);
            syncConfigAndData(config);
        }
    }

    public synchronized void initUserData(SeacraftPirateRiotConfig config) {
        // 配置
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return;
        }
        // 已有数据
        if (userData != null && userData.getActivityId() == config.getActivityInfo().getActivityId()) {
            return;
        }

        SeacraftPirateRiotUserData tmpData = new SeacraftPirateRiotUserData();
        tmpData.setActivityId(config.getActivityInfo().getActivityId());
        tmpData.setUserId(getUserId());
        tmpData.setEnergy(config.getMaxEnergy());
        tmpData.setLastRecTime(System.currentTimeMillis());
        tmpData.setMonsterManual(new ArrayList<>());
        tmpData.setRewardMonster(new ArrayList<>());
        tmpData.setScore(0L);
        tmpData.setEquipLevel(0);
        tmpData.setBattlePatronsMap(new ConcurrentHashMap<>());
        // 关卡相关
        tmpData.setBlockList(new int[SeacraftPirateRiotEliminateLogic.MAX_HEIGHT][SeacraftPirateRiotEliminateLogic.MAX_WIDTH]);
        tmpData.setChapterId(0);
        tmpData.setChapterStatus(ePirateRiotChapterStatus.FINISH.getValue());
        tmpData.setSectionId(0);
        tmpData.setMonsterList(new ArrayList<>());
        tmpData.setGuideIndex(0);

        // 刷新门客
        refreshBattlePatrons(config, tmpData);

        tmpData.setInsertOption();

        userData = tmpData;
    }

    public void syncConfigAndData(SeacraftPirateRiotConfig config) {
        // 配置
        SeacraftPirateRiotProto.SeacraftPirateRiotSyncConfigRespMsg.Builder builder = SeacraftPirateRiotPb.parseSeacraftPirateRiotSyncConfigRespMsg(config, player.getLanguage());
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_SYNC_CONFIG, builder);

        // 数据
        SeacraftPirateRiotUserData data = getUserData();
        if (data != null) {
            SeacraftPirateRiotProto.SeacraftPirateRiotSyncDataRespMsg.Builder builder1 = SeacraftPirateRiotProto.SeacraftPirateRiotSyncDataRespMsg.newBuilder();
            builder1.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
            player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_SYNC_DATA, builder1);
        }
    }

    public SeacraftPirateRiotUserData getUserData() {
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (userData == null && config != null) {
            initUserData(config);
        }
        if (userData != null && config != null) {
            // 算体力
            CalcPowerResult powerResult = PowerHelper.calcPower(userData.getLastRecTime() / 1000, config.getEnergyRecTime() / 1000, config.getEnergyRecCount(), config.getMaxEnergy(), userData.getEnergy());
            if (userData.getEnergy() != powerResult.getCurPower()) {
                // 日志
//                LogMgr.addLogSeacraftPirateRiotEnergy(new LogSeacraftPirateRiotEnergy(
//                        getUserId(),
//                        config.getActivityInfo().getActivityId(),
//                        1,
//                        powerResult.getCurPower() - userData.getEnergy(),
//                        powerResult.getCurPower(),
//                        4,
//                        -1,
//                        -1
//                ));
                // 日志
                AutoLogMgr.add(new LogSeacraftPirateRiotEnergy(
                        getUserId(),
                        config.getActivityInfo().getActivityId(),
                        1,
                        powerResult.getCurPower() - userData.getEnergy(),
                        powerResult.getCurPower(),
                        4,
                        -1,
                        -1
                ));
            }
            userData.setLastRecTime(powerResult.getLastRecoverTime() * 1000);
            userData.setEnergy(powerResult.getCurPower());
        }
        return userData;
    }

    // ===================================================== 【关卡】 ==============================================================

    private void chapterChange(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data, int chapterId) {
        SeacraftPirateRiotChapterConfig chapterConfig = config.getChapterConfigMap().get(chapterId);
        if (chapterConfig != null) {
            data.setChapterId(chapterId);
            data.setChapterStatus(ePirateRiotChapterStatus.NO_START.getValue());
        }
    }

    private void chapterStart(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data) {
        // 判断是否生成引导数据
        boolean isGuide = data.getChapterId() == 1 && data.getGuideIndex() == 0;
        if (isGuide) {
            chapterStartGuide(config, data);
            return;
        }

        // 正常生成数据
        data.setBlockList(SeacraftPirateRiotMgr.getRandomBlockList());
        refreshBattlePatrons(config, data);
        sectionChange(config, data, 1);
        data.setChapterStatus(ePirateRiotChapterStatus.IN_PROGRESS.getValue());
    }

    /**
     * 开始新手引导
     */
    private void chapterStartGuide(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data) {
        // 引导盘面
        int[][] guideBlockList = SeacraftPirateRiotMgr.getGuideBlockList(config);
        data.setBlockList(guideBlockList);
        data.setGuideIndex(guideBlockList.length * guideBlockList[0].length); // 6x7=42

        refreshBattlePatrons(config, data);
        sectionChange(config, data, 1);

        // 引导怪物数值修改
        if (!data.getMonsterList().isEmpty()) {
            SeacraftPirateRiotMonsterData monsterData = data.getMonsterList().get(0);
            monsterData.setAtkCd(1); // 下回合就砍你
        }
        // 引导门客蓝量设置
        long mp = config.getPatronsMaxMp() - config.getBlockMp() * (config.getInitBlock().length - data.getGuideIndex()); // 总蓝量 - 单个蓝量 x 需要引导的数量
        data.getBattlePatronsMap().get(4).setMp(mp); // 下下回合就放大招

        data.setChapterStatus(ePirateRiotChapterStatus.IN_PROGRESS.getValue());
    }

    private void chapterFail(SeacraftPirateRiotUserData data) {
        data.setChapterStatus(ePirateRiotChapterStatus.NO_START.getValue());
    }

    private void sectionChange(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data, int sectionId) {
        // 拿配置
        SeacraftPirateRiotSectionConfig sectionConfig = config.getSectionConfigMap().get(data.getChapterId()).get(sectionId);
        // 怪物生成
        List<SeacraftPirateRiotMonsterData> monsterDataList = new ArrayList<>();
        for (int i = 0; i < sectionConfig.getMonsterList().size(); i++) {
            int id = sectionConfig.getMonsterList().get(i);
            int height = sectionConfig.getHeightList().get(i);
            int left = sectionConfig.getLeftList().get(i);
            int right = sectionConfig.getRightList().get(i);
            long hpMax = sectionConfig.getHealthList().get(i);
            long atk = sectionConfig.getAttackList().get(i);
            SeacraftPirateRiotMonsterConfig monsterConfig = config.getMonsterConfigMap().get(id);
            long mpMax = hpMax * monsterConfig.getAnger() / 1000;
            int atkCd = monsterConfig.getAttackCd();
            monsterDataList.add(new SeacraftPirateRiotMonsterData(id, i, hpMax, mpMax, atkCd, atk, left, right, height));
        }

        // 设置数据
        data.setSectionId(sectionId);
        data.setMonsterList(monsterDataList);
    }

    /**
     * 判断关卡，并修改状态
     */
    private SeacraftPirateRiotCheckChapterResult checkChapter(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data) {

        // 是否失败
        if (isChapterFail(data)) {
            chapterFail(data);
            return new SeacraftPirateRiotCheckChapterResult(2, config.getChapterConfigMap().get(data.getChapterId()).getFailReward());
        }

        // 怪物全部击杀
        if (monsterIsAllDead(data)) {
            if (hasNextSection(config, data)) {
                // 切换下一波
                sectionChange(config, data, data.getSectionId() + 1);
                return new SeacraftPirateRiotCheckChapterResult(3, "0");
            } else {
                // 通关
                SeacraftPirateRiotChapterConfig chapterConfig = config.getChapterConfigMap().get(data.getChapterId());
                if ("0".equals(chapterConfig.getFirstReward())) {
                    data.setChapterStatus(ePirateRiotChapterStatus.FINISH.getValue());
                } else {
                    data.setChapterStatus(ePirateRiotChapterStatus.WAIT_REWARD.getValue());
                }

                // 更新下榜单（这里只是更新关卡，等会儿还会更新积分的）
                player.notifyListener(eGamePlayerEventType.SeacraftPirateRiotScoreRank.getValue(), new SeacraftPirateRiotScoreRankArgs(data.getScore(), SeacraftPirateRiotMgr.getRankChapterId(data)));

                // 更新最大关卡
                if (chapterConfig.getChapterId() > player.getModule(AttributeModule.class).getAttribute(ePlayerAttrType.SeacraftPirateRiotMaxChapterId)) {
                    player.getModule(AttributeModule.class).setAttribute(ePlayerAttrType.SeacraftPirateRiotMaxChapterId, chapterConfig.getChapterId());
                }

                // 组装奖励（固定奖励+随机奖励）

                Property rewardProp = PropertyHelper.parseStringToProperty(chapterConfig.getReward());

                List<Integer> weight = new ArrayList<>();
                List<String> reward = new ArrayList<>();
                for (String s : StringUtils.stringToStringList(chapterConfig.getRandomReward(), "\\|")) {
                    List<String> list = StringUtils.stringToStringList(s, ";");
                    reward.add(list.get(0));
                    weight.add(Integer.valueOf(list.get(1)));
                }
                if (!weight.isEmpty()) {
                    int index = randomHelper.next(weight.size());
                    rewardProp.addProperty(PropertyHelper.parseStringToProperty(reward.get(index)));
                }

                return new SeacraftPirateRiotCheckChapterResult(1, PropertyHelper.parsePropertyToString(rewardProp));
            }
        }

        // 无事
        return new SeacraftPirateRiotCheckChapterResult(0, "0");
    }

    private boolean isChapterFail(SeacraftPirateRiotUserData data) {
        for (SeacraftPirateRiotBattlePatrons battlePatrons : data.getBattlePatronsMap().values()) {
            if (battlePatrons.getHp() > 0) {
                return false;
            }
        }
        return true;
    }

    private boolean hasNextSection(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data) {
        Map<Integer, SeacraftPirateRiotSectionConfig> sectionConfigMap = config.getSectionConfigMap().get(data.getChapterId());
        if (sectionConfigMap == null) {
            return false;
        }
        return sectionConfigMap.containsKey(data.getSectionId() + 1);
    }

    // ===================================================== 【CMD】 ==============================================================

    /**
     * 移动关卡
     */
    public int move(int chapterId) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 关卡判断
        if (data.getChapterStatus() != ePirateRiotChapterStatus.FINISH.getValue()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_NO_FINISH; // 未完成
        }
        int nextChapterId = data.getChapterId() + 1;
        if (chapterId != nextChapterId) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_ID_ERROR; // ID不是下一关的
        }
        if (!config.getChapterConfigMap().containsKey(nextChapterId)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_NEXT_CHAPTER; // 没有下一关了
        }

        // 修改数据
        chapterChange(config, data, chapterId);
        return 0;
    }

    /**
     * 开始关卡
     */
    public int startChapter(int chapterId, int skip) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 系统解锁判断
        if (!SystemOpenMgr.systemOpen(player, eSystemId.Seacraft.getValue())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 关卡状态判断
        if (chapterId != data.getChapterId()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_ID_ERROR;
        }
        if (ePirateRiotChapterStatus.NO_START.getValue() != data.getChapterStatus()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_STATUS_ERROR;
        }

        // 扣体力
        SeacraftPirateRiotChapterConfig chapterConfig = config.getChapterConfigMap().get(chapterId);
        if (!removeEnergy(config, chapterConfig.getEnergy())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_ENERGY_NO_ENOUGH;
        }

        // 日志
//        LogMgr.addLogSeacraftPirateRiotEnergy(new LogSeacraftPirateRiotEnergy(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                0,
//                chapterConfig.getEnergy(),
//                data.getEnergy(),
//                1,
//                chapterId,
//                -1
//        ));
        // 日志
        AutoLogMgr.add(new LogSeacraftPirateRiotEnergy(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                0,
                chapterConfig.getEnergy(),
                data.getEnergy(),
                1,
                chapterId,
                -1
        ));

        // 【看下是不是直接通关】
        if (skip == -1 || skip == 1) {
            //chapterId != 1 && 7.2优化 可以跳过第一章
            if ( chapterId <= player.getModule(AttributeModule.class).getAttribute(ePlayerAttrType.SeacraftPirateRiotMaxChapterId)) {
                long showAvgAttack = SeacraftPirateRiotMgr.getShowAvgAttack(data);
                long showAvgHp = SeacraftPirateRiotMgr.getShowAvgHp(data);
                long passAttack = chapterConfig.getAdviceAttack() * config.getAutoPassParamList().get(0) / 1000; //
                long passHp = chapterConfig.getAdviceHp() * config.getAutoPassParamList().get(1) / 1000; //
                if (showAvgAttack >= passAttack && showAvgHp >= passHp) {
                    // 直接通关
                    autoPass(data, config, chapterId);
                    return 0;
                }
            }
        }

        // 如果要自动通关又通不了，返回错误码
        if (skip == 1) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CAN_NOT_AUTO_PASS;
        }

        // 修改数据
        chapterStart(config, data);
        // 返回
        SeacraftPirateRiotProto.SeacraftPirateRiotStartChapterRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotStartChapterRespMsg.newBuilder();
        builder.setRet(0);
        SeacraftPirateRiotUserData userData = player.getModule(SeacraftPirateRiotModule.class).getUserData();
        if (userData != null) {
            builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, userData));
            builder.setChapterData(SeacraftPirateRiotPb.parseSeacraftPirateRiotChapterMsg(userData));
        }
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_START_CHAPTER, builder);
        return 0;
    }

    private void autoPass(SeacraftPirateRiotUserData data, SeacraftPirateRiotConfig config, int chapterId) {
        SeacraftPirateRiotChapterConfig chapterConfig = config.getChapterConfigMap().get(chapterId);
        Map<Integer, SeacraftPirateRiotSectionConfig> sectionConfigMap = config.getSectionConfigMap().get(chapterId);

        boolean isBoss = !"0".equals(chapterConfig.getFirstReward());

        // 设置关卡状态
        data.setChapterId(chapterId);
        data.setChapterStatus(isBoss ? ePirateRiotChapterStatus.WAIT_REWARD.getValue() : ePirateRiotChapterStatus.FINISH.getValue());

        // 加图鉴
        for (SeacraftPirateRiotSectionConfig sectionConfig : sectionConfigMap.values()) {
            for (Integer monsterId : sectionConfig.getMonsterList()) {
                if (!data.getMonsterManual().contains(monsterId)) {
                    data.getMonsterManual().add(monsterId);
                }
            }
        }

        // 【拿奖励】
        Property rewardProp = new Property();
        // 1.通关boss关大奖（这个不要了，让他手动领）
        // 2.固定奖励
        rewardProp.addProperty(PropertyHelper.parseStringToProperty(chapterConfig.getReward()));
        // 3. 随机奖励
        List<Integer> weight = new ArrayList<>();
        List<String> reward = new ArrayList<>();
        for (String s : StringUtils.stringToStringList(chapterConfig.getRandomReward(), "\\|")) {
            List<String> list = StringUtils.stringToStringList(s, ";");
            reward.add(list.get(0));
            weight.add(Integer.valueOf(list.get(1)));
        }
        if (!weight.isEmpty()) {
            int index = randomHelper.next(weight.size());
            rewardProp.addProperty(PropertyHelper.parseStringToProperty(reward.get(index)));
        }
        String rewardStr = PropertyHelper.parsePropertyToString(rewardProp);

        // 加奖励
        player.getModule(CurrencyModule.class).addCurrency(rewardProp, eLogMoneyType.SeacraftPirateRiot, eLogMoneyType.SeacraftPirateRiotAutoPassReward);

        // 加榜单积分、上榜
        addRankScore(config, data, rewardStr);

        // 返回
        SeacraftPirateRiotProto.SeacraftPirateRiotStartChapterRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotStartChapterRespMsg.newBuilder();
        builder.setRet(0);
        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        builder.setChapterData(SeacraftPirateRiotPb.parseSeacraftPirateRiotChapterMsg(data));
        builder.setReward(rewardStr);
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_START_CHAPTER, builder);

        // 日志
//        LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newAutoPass(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                data.getChapterId(),
//                data.getEquipLevel(),
//                SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
//                SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
//                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
//                rewardStr
//        ));
        // 日志
        AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newAutoPass(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                data.getChapterId(),
                data.getEquipLevel(),
                SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
                SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
                rewardStr
        ));
    }

    /**
     * 获取关卡数据
     */
    public int getChapterData(int chapterId) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 关卡ID错误
        if (data.getChapterId() != chapterId) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_ID_ERROR;
        }

        // 进关卡前刷新
        refreshBattlePatrons(config, data);

        // 返回
        SeacraftPirateRiotProto.SeacraftPirateRiotGetChapterRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotGetChapterRespMsg.newBuilder();
        builder.setRet(0);
        builder.setChapterData(SeacraftPirateRiotPb.parseSeacraftPirateRiotChapterMsg(data));
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_GET_CHAPTER, builder);
        return 0;
    }

    /**
     * 扫荡
     */
    public int fastPassChapter(int chapterId, int times) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 关卡是否已通关
        if (chapterId > data.getChapterId() ||
                chapterId == data.getChapterId() && data.getChapterStatus() != ePirateRiotChapterStatus.FINISH.getValue()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_FAST_NO_PASSED;
        }

        // 关卡是否存在
        SeacraftPirateRiotChapterConfig chapterConfig = config.getChapterConfigMap().get(chapterId);
        if (chapterConfig == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_ID_ERROR;
        }

        // times限制
        if (times > 9999 || times < 1) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PARAM_ERROR;
        }

        // 计算消耗、奖励
        int costEnergy = chapterConfig.getEnergy() * times;
        Property reward = PropertyHelper.parseStringToProperty(chapterConfig.getReward());
        reward.rideProperty(times);

        // 加随机奖励
        List<Integer> weightConfig = new ArrayList<>();
        List<String> rewardConfig = new ArrayList<>();
        for (String s : StringUtils.stringToStringList(chapterConfig.getRandomReward(), "\\|")) {
            List<String> list = StringUtils.stringToStringList(s, ";");
            rewardConfig.add(list.get(0));
            weightConfig.add(Integer.valueOf(list.get(1)));
        }
        if (!weightConfig.isEmpty()) {
            for (int i = 0; i < times; i++) {
                int index = randomHelper.next(weightConfig.size());
                reward.addProperty(PropertyHelper.parseStringToProperty(rewardConfig.get(index)));
            }
        }

        String rewardStr = PropertyHelper.parsePropertyToString(reward);

        // 扣体力
        if (!removeEnergy(config, costEnergy)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_ENERGY_NO_ENOUGH;
        }

        // 加积分
        addRankScore(config, data, rewardStr);

        // 加奖励
        player.getModule(CurrencyModule.class).addCurrency(reward, eLogMoneyType.SeacraftPirateRiot, eLogMoneyType.SeacraftPirateRiotFastGet);

        // 返回
        SeacraftPirateRiotProto.SeacraftPirateRiotFastRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotFastRespMsg.newBuilder();
        builder.setRet(0);
        builder.setReward(rewardStr);
        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_FAST, builder);

        data.setUpdateOption();

//        // 日志
//        LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newFast(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                chapterId,
//                times,
//                rewardStr
//        ));
//        LogMgr.addLogSeacraftPirateRiotEnergy(new LogSeacraftPirateRiotEnergy(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                0,
//                costEnergy,
//                data.getEnergy(),
//                2,
//                chapterId,
//                times
//        ));

        // 日志
        AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newFast(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                chapterId,
                times,
                rewardStr
        ));
        AutoLogMgr.add(new LogSeacraftPirateRiotEnergy(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                0,
                costEnergy,
                data.getEnergy(),
                2,
                chapterId,
                times
        ));

        return 0;
    }

    /**
     * 领取图鉴奖励
     */
    public int getMonsterManualReward(int monsterId) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 拿配置
        SeacraftPirateRiotMonsterConfig monsterConfig = config.getMonsterConfigMap().get(monsterId);
        if (monsterConfig == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PARAM_ERROR;
        }

        // 是否击杀过
        if (!data.getMonsterManual().contains(monsterId)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_MONSTER_NO_KILL;
        }

        // 是否领取过
        if (data.getRewardMonster().contains(monsterId)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_HAD_GET_REWARD;
        }

        // 拿奖励
        String rewardStr = monsterConfig.getFirstReward();
        Property rewardProp = PropertyHelper.parseStringToProperty(rewardStr);

        // 改状态
        data.getRewardMonster().add(monsterId);

        // 加奖励
        player.getModule(CurrencyModule.class).addCurrency(rewardProp, eLogMoneyType.SeacraftPirateRiot, eLogMoneyType.SeacraftPirateRiotMonsterManualReward);

        // 返回
        SeacraftPirateRiotProto.SeacraftPirateRiotGetRewardRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotGetRewardRespMsg.newBuilder();
        builder.setRet(0);
        builder.setReward(rewardStr);
        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_GET_REWARD, builder);

        data.setUpdateOption();
        return 0;
    }

    /**
     * 获取大关卡首通随机奖励
     */
    public int getChapterFirstReward(int chapterId) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 判断关卡
        if (data.getChapterId() != chapterId) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_ID_ERROR; // ID错误
        }
        SeacraftPirateRiotChapterConfig chapterConfig = config.getChapterConfigMap().get(chapterId);
        if (chapterConfig == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_ID_ERROR; // 配置有问题
        }
        if (data.getChapterStatus() != ePirateRiotChapterStatus.WAIT_REWARD.getValue()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_STATUS_ERROR; // 状态不对
        }
        if ("0".equals(chapterConfig.getFirstReward())) {
            log.info("getChapterFirstReward error! activityId {} , userId {} , chapterId {} , status is waitReward but no rewardConfig! fix status to finish.",
                    config.getActivityInfo().getActivityId(), getUserId(), data.getChapterId());
            data.setChapterStatus(ePirateRiotChapterStatus.FINISH.getValue());
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PARAM_ERROR;
        }

        // 拿奖励
        String rewardStr = chapterConfig.getFirstReward();
        Property reward = PropertyHelper.parseStringToProperty(rewardStr);

        // 改状态
        data.setChapterStatus(ePirateRiotChapterStatus.FINISH.getValue());

        // 加奖励
        player.getModule(CurrencyModule.class).addCurrency(reward, eLogMoneyType.SeacraftPirateRiot, eLogMoneyType.SeacraftPirateRiotChapterFirstReward);

        // 返回
        SeacraftPirateRiotProto.SeacraftPirateRiotGetRewardRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotGetRewardRespMsg.newBuilder();
        builder.setRet(0);
        builder.setReward(rewardStr);
        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_GET_REWARD, builder);

        data.setUpdateOption();

        // 日志
//        LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newFirst(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                chapterId,
//                rewardStr
//        ));
        // 日志
        AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newFirst(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                chapterId,
                rewardStr
        ));

        return 0;
    }

    /**
     * 消除
     */
    public int eliminate(int fromX, int fromY, int toX, int toY) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 关卡校验
        if (data.getChapterStatus() != ePirateRiotChapterStatus.IN_PROGRESS.getValue()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_STATUS_ERROR;
        }

        // 日志
        int logChapterId = data.getChapterId();
        int logSectionId = data.getSectionId();
        List<String> logMonster = new ArrayList<>();
        logMonster.add(SeacraftPirateRiotLogHelper.parseMonsterCurHp(data.getMonsterList()));
        logMonster.add("-1");
        logMonster.add("-1");

        // 消除
        SeacraftPirateRiotEliminateResult eliminateResult = SeacraftPirateRiotEliminateLogic.eliminateBlock(config, data, 1, fromX, fromY, toX, toY);

        // 是否消除失败
        if (eliminateResult.getRet() != 0) {
            return eliminateResult.getRet();
        }

        // 返回值
        SeacraftPirateRiotProto.SeacraftPirateRiotEliminateRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotEliminateRespMsg.newBuilder();
        builder.setRet(0);

        // 获取门客数据
        Map<Integer, SeacraftPirateRiotBattlePatrons> occupationBattleMap = data.getBattlePatronsMap();

        // 方块效果处理
        builder.addAllRoundMsg(
                handleEliminateResult(eliminateResult, config, data, occupationBattleMap, logMonster));
        addMonsterManual(data);

        // 怪物回合攻击
        builder.addAllMonsterAttack(
                handleMonsterAttack(config, data, occupationBattleMap)
        );
        addMonsterManual(data);

        // 下个回合
        roundReduce(data);

        // 日志
        logMonster.set(2, SeacraftPirateRiotLogHelper.parseMonsterCurHp(data.getMonsterList()));

        // 处理波次完成
        SeacraftPirateRiotCheckChapterResult chapterResult = checkChapter(config, data);
        builder.setRewardType(chapterResult.getType());
        // 1通关 || 2失败
        if (chapterResult.getType() == 1 || chapterResult.getType() == 2) {
            if (!"0".equals(chapterResult.getReward())) {
                player.getModule(CurrencyModule.class).addCurrency(chapterResult.getReward(), eLogMoneyType.SeacraftPirateRiot,
                        chapterResult.getType() == 1 ? eLogMoneyType.SeacraftPirateRiotChapterPassReward : eLogMoneyType.SeacraftPirateRiotChapterFailReward);
                addRankScore(config, data, chapterResult.getReward());
            }
            builder.setReward(chapterResult.getReward());

            // 日志
            if (chapterResult.getType() == 1) {
//                LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newPass(
//                        getUserId(),
//                        config.getActivityInfo().getActivityId(),
//                        data.getChapterId(),
//                        data.getEquipLevel(),
//                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
//                        chapterResult.getReward()
//                ));
                AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newPass(
                        getUserId(),
                        config.getActivityInfo().getActivityId(),
                        data.getChapterId(),
                        data.getEquipLevel(),
                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
                        chapterResult.getReward()
                ));
            } else if (chapterResult.getType() == 2) {
//                LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newDead(
//                        getUserId(),
//                        config.getActivityInfo().getActivityId(),
//                        data.getChapterId(),
//                        data.getEquipLevel(),
//                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
//                        chapterResult.getReward()
//                ));
                AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newDead(
                        getUserId(),
                        config.getActivityInfo().getActivityId(),
                        data.getChapterId(),
                        data.getEquipLevel(),
                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
                        chapterResult.getReward()
                ));
            }

        }

        // 波次奖励MP
        if (chapterResult.getType() == 3) {
            List<SeacraftPirateRiotChange> mpChangeList = sectionChangeRewardMp(config, data);
            for (SeacraftPirateRiotChange change : mpChangeList) {
                builder.addMpChangeList(SeacraftPirateRiotPb.parseSeacraftPirateRiotChangeMsg(change)); // 在关卡数据之前
            }
        }

        // 当前关卡数据
        builder.setChapterData(SeacraftPirateRiotPb.parseSeacraftPirateRiotChapterMsg(data));

        // 返回
        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_ELIMINATE, builder);

        data.setUpdateOption();

        // 日志
//        LogMgr.addLogSeacraftPirateRiotEliminate(new LogSeacraftPirateRiotEliminate(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                logChapterId,
//                logSectionId,
//                logMonster.get(0),
//                0,
//                -1,
//                SeacraftPirateRiotLogHelper.parseFirstRoundEliminateCount(eliminateResult),
//                SeacraftPirateRiotLogHelper.parseFirstRoundBoomUse(eliminateResult),
//                SeacraftPirateRiotLogHelper.parseFirstRoundDamage(builder.getRoundMsgList()),
//                logMonster.get(1),
//                SeacraftPirateRiotLogHelper.parseOtherRoundEliminateCount(eliminateResult),
//                SeacraftPirateRiotLogHelper.parseOtherRoundDamage(builder.getRoundMsgList()),
//                logMonster.get(2),
//                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap())
//        ));
        AutoLogMgr.add(new LogSeacraftPirateRiotEliminate(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                logChapterId,
                logSectionId,
                logMonster.get(0),
                0,
                -1,
                SeacraftPirateRiotLogHelper.parseFirstRoundEliminateCount(eliminateResult),
                SeacraftPirateRiotLogHelper.parseFirstRoundBoomUse(eliminateResult),
                SeacraftPirateRiotLogHelper.parseFirstRoundDamage(builder.getRoundMsgList()),
                logMonster.get(1),
                SeacraftPirateRiotLogHelper.parseOtherRoundEliminateCount(eliminateResult),
                SeacraftPirateRiotLogHelper.parseOtherRoundDamage(builder.getRoundMsgList()),
                logMonster.get(2),
                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap())
        ));

        return 0;
    }

    private List<SeacraftPirateRiotChange> sectionChangeRewardMp(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data) {
        long rewardMp = config.getSectionRewardMp();
        List<SeacraftPirateRiotChange> changeList = new ArrayList<>();
        for (SeacraftPirateRiotBattlePatrons battlePatrons : data.getBattlePatronsMap().values()) {
            if (battlePatrons.getHp() > 0) {
                long addValue = rewardMp;
                SeacraftPirateRiotChange change = new SeacraftPirateRiotChange(battlePatrons.getId(), null, addValue, null);
                changeList.add(change);
                applyPatronsChange(battlePatrons, change);
            }
        }
        return changeList;
    }

    /**
     * 使用体力药
     */
    public int useEnergyItem(int count) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 系统解锁判断
        if (!SystemOpenMgr.systemOpen(player, eSystemId.Seacraft.getValue())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 使用数量
        if (count > 9999 || count < 1) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PARAM_ERROR;
        }

        // 配置
        GoodsInfo goodsInfo = GoodsMgr.getGoodsById(config.getEnergyItemId());
        if (goodsInfo == null) {
            return GameErrorCode.E_GOODS_NO_EXIST;
        }

        // 扣消耗
        Property cost = PropertyHelper.parseStringToProperty(config.getEnergyItemId() + "=" + count);
        if (!player.getModule(CurrencyModule.class).removeCurrency(cost, eLogMoneyType.SeacraftPirateRiot, eLogMoneyType.SeacraftPirateRiotUseEnergyItem)) {
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        // 加体力
        int recCount = count * goodsInfo.getParamList().get(0).intValue();
        data.setEnergy(data.getEnergy() + recCount);
        if (data.getEnergy() > config.getMaxEnergy()) {
            data.setLastRecTime(System.currentTimeMillis());
        }

        data.setUpdateOption();

        // 日志
//        LogMgr.addLogSeacraftPirateRiotEnergy(new LogSeacraftPirateRiotEnergy(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                1,
//                recCount,
//                data.getEnergy(),
//                3,
//                -1,
//                -1
//        ));

        AutoLogMgr.add(new LogSeacraftPirateRiotEnergy(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                1,
                recCount,
                data.getEnergy(),
                3,
                -1,
                -1
        ));

        return 0;
    }

    /**
     * 获取消除提示
     */
    public void getEliminateTips() {
        // 活动时间判断
        long now = System.currentTimeMillis();
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return;
        }

        // 防止频繁请求，客户端间隔1秒
        if (now - data.getLastTipsTime() < 1000) {
            return;
        }
        data.setLastTipsTime(now);

        SeacraftPirateRiotEliminateTips tips = SeacraftPirateRiotEliminateLogic.calSeacraftPirateRiotEliminateTips(data.getBlockList());
        if (tips.isSuccess()) {
            SeacraftPirateRiotProto.SeacraftPirateRiotGetEliminateTipsRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotGetEliminateTipsRespMsg.newBuilder();
            builder.setRet(0);
            builder.setFromBlock(SeacraftPirateRiotPb.parseSeacraftPirateBlockMsg(tips.getFrom()));
            if (tips.getTo() != null) {
                builder.setToBlock(SeacraftPirateRiotPb.parseSeacraftPirateBlockMsg(tips.getTo()));
            }
            for (SeacraftPirateRiotBlock block : tips.getBlockList()) {
                builder.addBlockList(SeacraftPirateRiotPb.parseSeacraftPirateBlockMsg(block));
            }
            player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_GET_ELIMINATE_TIPS, builder);
        } else {
            log.info("SeacraftPirateRiot getTips false! activityId={}, userId={}, rearrange! blockList = {}", data.getActivityId(), data.getUserId(), JSON.toJSONString(data.getBlockList()));
            int[][] copyBlockList = SeacraftPirateRiotMgr.getCopyBlockList(data.getBlockList());
            SeacraftPirateRiotEliminateLogic.rearrange(copyBlockList);
            data.setBlockList(copyBlockList);
        }
    }

    /**
     * 使用技能
     */
    public int useSkill(int patronsId, int param) {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 判断关卡状态
        if (data.getChapterStatus() != ePirateRiotChapterStatus.IN_PROGRESS.getValue()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_STATUS_ERROR;
        }

        // 判断门客
        PatronsInfo patronsInfo = PatronsMgr.getPatronsInfo(patronsId);
        if (patronsInfo == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PATRONS_NO_BATTLE;
        }
        SeacraftPirateRiotBattlePatrons patronsData = data.getBattlePatronsMap().get(patronsInfo.getOccupation());
        if (patronsData.getHp() <= 0) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PATRONS_HAD_DEAD;
        }
        if (patronsData.getMp() < patronsData.getMpMax()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PATRONS_MP_NO_ENOUGH;
        }

        // 技能参数判断
        if (patronsInfo.getOccupation() == 5) {
            if (param >= data.getMonsterList().size()) {
                return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_PARAM_ERROR;
            }
            if (data.getMonsterList().get(param).getHp() <= 0) {
                return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_MONSTER_HAD_DEAD;
            }
        }

        // 返回值
        SeacraftPirateRiotProto.SeacraftPirateRiotUseSkillRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotUseSkillRespMsg.newBuilder();
        builder.setRet(0);

        // 扣能量
        SeacraftPirateRiotChange useSkillMpChange = new SeacraftPirateRiotChange(patronsData.getId(), null, -1 * patronsData.getMp(), null);
        patronsData.setMp(0);
        builder.addChangeList(SeacraftPirateRiotPb.parseSeacraftPirateRiotChangeMsg(useSkillMpChange));

        // 日志
        int logChapterId = data.getChapterId();
        int logSectionId = data.getSectionId();
        List<String> logMonster = new ArrayList<>();
        logMonster.add(SeacraftPirateRiotLogHelper.parseMonsterCurHp(data.getMonsterList()));
        logMonster.add("-1");
        logMonster.add("-1");

        int skillType = PatronsMgr.getPatronsInfo(patronsData.getId()).getOccupation();
        List<SeacraftPirateRiotChange> changeList = new ArrayList<>();

        if (skillType != 4) {
            // 释放技能（1/2）
            skillEffect(config, skillType, param, data, changeList);
            for (SeacraftPirateRiotChange change : changeList) {
                builder.addChangeList(SeacraftPirateRiotPb.parseSeacraftPirateRiotChangeMsg(change));
            }
            builder.setSkillChapterData(SeacraftPirateRiotPb.parseSeacraftPirateRiotChapterMsg(data));
        }

        // 消方块
        SeacraftPirateRiotEliminateResult eliminateResult = SeacraftPirateRiotEliminateLogic.eliminateBlockByColor(config, data, 1, skillType);

        // 方块效果处理
        builder.addAllRoundMsg(
                handleEliminateResult(eliminateResult, config, data, data.getBattlePatronsMap(), logMonster));
        addMonsterManual(data);

        // 处理波次完成
        SeacraftPirateRiotCheckChapterResult chapterResult = checkChapter(config, data);
        builder.setRewardType(chapterResult.getType());
        // 1通关 || 2失败
        if (chapterResult.getType() == 1 || chapterResult.getType() == 2) {
            if (!"0".equals(chapterResult.getReward())) {
                player.getModule(CurrencyModule.class).addCurrency(chapterResult.getReward(), eLogMoneyType.SeacraftPirateRiot,
                        chapterResult.getType() == 1 ? eLogMoneyType.SeacraftPirateRiotChapterPassReward : eLogMoneyType.SeacraftPirateRiotChapterFailReward);
                addRankScore(config, data, chapterResult.getReward());
            }
            builder.setReward(chapterResult.getReward());

            // 日志
            if (chapterResult.getType() == 1) {
//                LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newPass(
//                        getUserId(),
//                        config.getActivityInfo().getActivityId(),
//                        data.getChapterId(),
//                        data.getEquipLevel(),
//                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
//                        chapterResult.getReward()
//                ));
                AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newPass(
                        getUserId(),
                        config.getActivityInfo().getActivityId(),
                        data.getChapterId(),
                        data.getEquipLevel(),
                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
                        chapterResult.getReward()
                ));
            } else if (chapterResult.getType() == 2) {
//                LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newDead(
//                        getUserId(),
//                        config.getActivityInfo().getActivityId(),
//                        data.getChapterId(),
//                        data.getEquipLevel(),
//                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
//                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
//                        chapterResult.getReward()
//                ));
                AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newDead(
                        getUserId(),
                        config.getActivityInfo().getActivityId(),
                        data.getChapterId(),
                        data.getEquipLevel(),
                        SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
                        SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
                        chapterResult.getReward()
                ));
            }

        }

        // 波次奖励MP
        if (chapterResult.getType() == 3) {
            List<SeacraftPirateRiotChange> mpChangeList = sectionChangeRewardMp(config, data);
            for (SeacraftPirateRiotChange change : mpChangeList) {
                builder.addMpChangeList(SeacraftPirateRiotPb.parseSeacraftPirateRiotChangeMsg(change)); // 在关卡数据之前
            }
        }

        builder.setChapterData(SeacraftPirateRiotPb.parseSeacraftPirateRiotChapterMsg(data));

        if (skillType == 4) {
            // 释放技能（2/2）
            skillEffect(config, skillType, param, data, changeList);
            for (SeacraftPirateRiotChange change : changeList) {
                builder.addChangeList(SeacraftPirateRiotPb.parseSeacraftPirateRiotChangeMsg(change));
            }
            builder.setSkillChapterData(SeacraftPirateRiotPb.parseSeacraftPirateRiotChapterMsg(data));
        }

        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_USE_SKILL, builder);

        data.setUpdateOption();

        // 日志
//        LogMgr.addLogSeacraftPirateRiotEliminate(new LogSeacraftPirateRiotEliminate(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                logChapterId,
//                logSectionId,
//                logMonster.get(0),
//                skillType,
//                SeacraftPirateRiotLogHelper.parseSkillDamage(changeList),
//                SeacraftPirateRiotLogHelper.parseFirstRoundEliminateCount(eliminateResult),
//                SeacraftPirateRiotLogHelper.parseFirstRoundBoomUse(eliminateResult),
//                SeacraftPirateRiotLogHelper.parseFirstRoundDamage(builder.getRoundMsgList()),
//                logMonster.get(1),
//                SeacraftPirateRiotLogHelper.parseOtherRoundEliminateCount(eliminateResult),
//                SeacraftPirateRiotLogHelper.parseOtherRoundDamage(builder.getRoundMsgList()),
//                logMonster.get(2),
//                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap())
//        ));
        // 日志
        AutoLogMgr.add(new LogSeacraftPirateRiotEliminate(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                logChapterId,
                logSectionId,
                logMonster.get(0),
                skillType,
                SeacraftPirateRiotLogHelper.parseSkillDamage(changeList),
                SeacraftPirateRiotLogHelper.parseFirstRoundEliminateCount(eliminateResult),
                SeacraftPirateRiotLogHelper.parseFirstRoundBoomUse(eliminateResult),
                SeacraftPirateRiotLogHelper.parseFirstRoundDamage(builder.getRoundMsgList()),
                logMonster.get(1),
                SeacraftPirateRiotLogHelper.parseOtherRoundEliminateCount(eliminateResult),
                SeacraftPirateRiotLogHelper.parseOtherRoundDamage(builder.getRoundMsgList()),
                logMonster.get(2),
                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap())
        ));

        return 0;
    }

    /**
     * 获取战备数据
     */
    public int getEquipData() {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (config == null || !ActivityMgr.activityInShowTime(config.getActivityInfo())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        refreshBattlePatrons(config, data);

        SeacraftPirateRiotProto.SeacraftPirateRiotGetEquipDataRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotGetEquipDataRespMsg.newBuilder();
        builder.setRet(0);
        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        for (SeacraftPirateRiotBattlePatrons battlePatrons : data.getBattlePatronsMap().values()) {
            builder.addPatronsData(SeacraftPirateRiotPb.parseSeacraftPirateRiotPatronsMsg(battlePatrons));
        }
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_GET_EQUIP_DATA, builder);
        return 0;
    }

    /**
     * 升级战备数据
     */
    public int upgradeEquipData() {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 系统解锁判断
        if (!SystemOpenMgr.systemOpen(player, eSystemId.Seacraft.getValue())) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        SeacraftPirateRiotEquipConfig curEquipConfig = config.getEquipConfigMap().get(data.getEquipLevel());
        SeacraftPirateRiotEquipConfig nextEquipConfig = config.getEquipConfigMap().get(data.getEquipLevel() + 1);
        if (curEquipConfig == null || nextEquipConfig == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_EQUIP_LEVEL_MAX;
        }

        // 扣消耗
        Property consume = PropertyHelper.parseStringToProperty(curEquipConfig.getConsume());
        PropertyHelper.removeZeroGoods(consume);
        if (!player.getModule(CurrencyModule.class).removeCurrency(consume, eLogMoneyType.SeacraftPirateRiot, eLogMoneyType.SeacraftPirateRiotUpgradeEquip)) {
            return GameErrorCode.E_BAG_ITEM_NO_ENOUGH;
        }

        // 加等级
        data.setEquipLevel(data.getEquipLevel() + 1);

        // 刷新下，返回值才是对的
        refreshBattlePatrons(config, data);

        // 日志
//        LogMgr.addLogSeacraftPirateRiotEquipUpgrade(new LogSeacraftPirateRiotEquipUpgrade(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                curEquipConfig.getConsume(),
//                data.getEquipLevel()
//        ));
        AutoLogMgr.add(new LogSeacraftPirateRiotEquipUpgrade(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                curEquipConfig.getConsume(),
                data.getEquipLevel()
        ));

        return 0;
    }

    /**
     * 取消关卡
     */
    public int cancelChapter() {
        // 活动时间判断
        SeacraftPirateRiotConfig config = SeacraftPirateRiotMgr.getPirateRiotConfig();
        if (!SeacraftPirateRiotMgr.pirateRiotInTime(config)) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_IN_TIME;
        }
        // 玩家数据判断
        SeacraftPirateRiotUserData data = getUserData();
        if (data == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_NO_UNLOCK;
        }

        // 关卡状态判断
        if (data.getChapterStatus() != ePirateRiotChapterStatus.IN_PROGRESS.getValue()) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_STATUS_ERROR;
        }

        // 获取失败奖励
        SeacraftPirateRiotChapterConfig chapterConfig = config.getChapterConfigMap().get(data.getChapterId());
        if (chapterConfig == null) {
            return GameErrorCode.E_SEACRAFT_PIRATE_RIOT_CHAPTER_ID_ERROR;
        }

        // 修改数据
        chapterFail(data);

        // 加奖励
        if (!"0".equals(chapterConfig.getFailReward())) {
            player.getModule(CurrencyModule.class).addCurrency(chapterConfig.getFailReward(), eLogMoneyType.SeacraftPirateRiot, eLogMoneyType.SeacraftPirateRiotChapterFailReward);
            addRankScore(config, data, chapterConfig.getFailReward());
        }

        // 返回
        SeacraftPirateRiotProto.SeacraftPirateRiotCancelChapterRespMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotCancelChapterRespMsg.newBuilder();
        builder.setRet(0);
        builder.setUserData(SeacraftPirateRiotPb.parseSeacraftPirateRiotUserDataMsg(player, data));
        builder.setReward(chapterConfig.getFailReward());
        player.sendPacket(ClientProtocol.U_SEACRAFT_PIRATE_RIOT_CANCEL_CHAPTER, builder);

        // 日志
//        LogMgr.addLogSeacraftPirateRiotChapterReward(LogSeacraftPirateRiotChapterReward.newCancel(
//                getUserId(),
//                config.getActivityInfo().getActivityId(),
//                data.getChapterId(),
//                data.getEquipLevel(),
//                SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
//                SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
//                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
//                chapterConfig.getFailReward()
//        ));
        AutoLogMgr.add(LogSeacraftPirateRiotChapterReward.newCancel(
                getUserId(),
                config.getActivityInfo().getActivityId(),
                data.getChapterId(),
                data.getEquipLevel(),
                SeacraftPirateRiotLogHelper.parsePatronsAtk(data.getBattlePatronsMap()),
                SeacraftPirateRiotLogHelper.parsePatronsMaxHp(data.getBattlePatronsMap()),
                SeacraftPirateRiotLogHelper.parsePatronsCurHp(data.getBattlePatronsMap()),
                chapterConfig.getFailReward()
        ));

        return 0;
    }

    // ===================================================== Skill ==============================================================

    /**
     * 海盗之乱技能1:增加全体门客攻击x，效果持续3回合。x=系数/1000*门客自身当前攻击
     */
    private List<SeacraftPirateRiotChange> skillType1(SeacraftPirateRiotConfig config, SeacraftPirateRiotBattlePatrons patronsData, List<SeacraftPirateRiotBattlePatrons> battlePatronsList) {
        SeacraftPirateRiotSkillConfig skillConfig = config.getSkillConfigMap().get(1);
        int percent = skillConfig.getParamList().get(0);
        long atkChangeValue = BigDecimal.valueOf(percent)
                .multiply(BigDecimal.valueOf(patronsData.getAtk()))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
        List<SeacraftPirateRiotChange> list = new ArrayList<>();
        for (SeacraftPirateRiotBattlePatrons data : battlePatronsList) {
            addBuffOrReplace(data.getBuffList(),
                    new SeacraftPirateRiotBuff(skillConfig.getSkillType(), skillConfig.getRound(), atkChangeValue, percent));
            list.add(new SeacraftPirateRiotChange(data.getId(), null, null, atkChangeValue));
        }
        return list;
    }

    /**
     * 海盗之乱技能2:全体门客恢复x生命值。x=系数/1000*门客自身最大生命值
     */
    private List<SeacraftPirateRiotChange> skillType2(SeacraftPirateRiotConfig config, SeacraftPirateRiotBattlePatrons patronsData, List<SeacraftPirateRiotBattlePatrons> battlePatronsList) {
        SeacraftPirateRiotSkillConfig skillConfig = config.getSkillConfigMap().get(2);
        int percent = skillConfig.getParamList().get(0);
        BigDecimal hpChangeValue = BigDecimal.valueOf(percent)
                .multiply(BigDecimal.valueOf(patronsData.getHpMax()))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN);
        List<SeacraftPirateRiotChange> list = new ArrayList<>();
        for (SeacraftPirateRiotBattlePatrons data : battlePatronsList) {
            SeacraftPirateRiotChange change = new SeacraftPirateRiotChange(data.getId(), hpChangeValue.longValue(), null, null);
            applyPatronsChange(data, change);
            list.add(change);
        }
        return list;
    }

    /**
     * 海盗之乱技能3:减少全体敌人x攻击,效果持续3回合。x=系数/1000*怪物当前攻击
     */
    private List<SeacraftPirateRiotChange> skillType3(SeacraftPirateRiotConfig config, List<SeacraftPirateRiotMonsterData> monsterDataList) {
        SeacraftPirateRiotSkillConfig skillConfig = config.getSkillConfigMap().get(3);
        int percent = -1 * skillConfig.getParamList().get(0);
        List<SeacraftPirateRiotChange> list = new ArrayList<>();
        for (SeacraftPirateRiotMonsterData data : monsterDataList) {
            if (data.getHp() <= 0) continue;
            long atkChangeValue = BigDecimal.valueOf(percent)
                    .multiply(BigDecimal.valueOf(data.getAtk()))
                    .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN).longValue();
            addBuffOrReplace(data.getBuffList(), new SeacraftPirateRiotBuff(skillConfig.getSkillType(), skillConfig.getRound(), atkChangeValue, percent));
            list.add(new SeacraftPirateRiotChange(data.getIndex(), null, null, Long.valueOf(percent)));
        }
        return list;
    }

    /**
     * 海盗之乱技能4：随机替换道具的方块数量n
     */
    private void skillType4(SeacraftPirateRiotConfig config, int[][] blockList) {
        // 取非炸弹格子
        List<Integer> xList = new ArrayList<>();
        List<Integer> yList = new ArrayList<>();
        for (int y = 0; y < blockList.length; y++) {
            for (int x = 0; x < blockList[y].length; x++) {
                if (!SeacraftPirateRiotEliminateLogic.isBomb(blockList[y][x])) {
                    xList.add(x);
                    yList.add(y);
                }
            }
        }

        // 取生成炸弹数量
        SeacraftPirateRiotSkillConfig skillConfig = config.getSkillConfigMap().get(4);
        int count = Math.min(skillConfig.getParamList().get(0), xList.size());

        for (int i = 0; i < count; i++) {
            if (xList.isEmpty() || yList.isEmpty()) {
                break; // 容错
            }

            // 取下标生成炸弹
            int index = randomHelper.next(xList.size());
            int x = xList.get(index);
            int y = yList.get(index);
            blockList[y][x] = randomHelper.next(2) == 0 ? 9 : 10; // 随机替换成九宫格或十字炸弹
            xList.remove(index);
            yList.remove(index);
        }
    }

    /**
     * 海盗之乱技能5:攻击指定敌人，造成x伤害。x=系数/1000*门客自身当前攻击
     */
    private List<SeacraftPirateRiotChange> skillType5(SeacraftPirateRiotConfig config, SeacraftPirateRiotBattlePatrons patronsData, SeacraftPirateRiotMonsterData monsterData) {
        SeacraftPirateRiotSkillConfig skillConfig = config.getSkillConfigMap().get(5);
        int percent = -1 * skillConfig.getParamList().get(0);
        BigDecimal hpChangeValue = BigDecimal.valueOf(percent)
                .multiply(BigDecimal.valueOf(calcBuffAtk(patronsData.getAtk(), patronsData.getBuffList())))
                .divide(BigDecimal.valueOf(1000), 0, BigDecimal.ROUND_DOWN);
        SeacraftPirateRiotChange change = new SeacraftPirateRiotChange(monsterData.getIndex(), hpChangeValue.longValue(), null, null);
        applyMonsterChange(monsterData, change);
        return Collections.singletonList(change);
    }

    /**
     * 添加buff（重复的就替换之前的）
     */
    private void addBuffOrReplace(List<SeacraftPirateRiotBuff> buffList, SeacraftPirateRiotBuff buff) {
        for (int i = 0; i < buffList.size(); i++) {
            if (buffList.get(i).getType() == buff.getType()) {
                buffList.remove(i);
                i--;
            }
        }
        buffList.add(buff);
    }

    private void skillEffect(SeacraftPirateRiotConfig config,
                             int skillType,
                             int param,
                             SeacraftPirateRiotUserData data,
                             List<SeacraftPirateRiotChange> changeList) {

        Map<Integer, SeacraftPirateRiotBattlePatrons> occupationBattleMap = data.getBattlePatronsMap();
        SeacraftPirateRiotBattlePatrons patronsData = occupationBattleMap.get(skillType);

        if (skillType == 1) {
            changeList.addAll(skillType1(config, patronsData, new ArrayList<>(occupationBattleMap.values())));
        } else if (skillType == 2) {
            changeList.addAll(skillType2(config, patronsData, new ArrayList<>(occupationBattleMap.values())));
        } else if (skillType == 3) {
            changeList.addAll(skillType3(config, data.getMonsterList()));
        } else if (skillType == 4) {
            skillType4(config, data.getBlockList());
        } else if (skillType == 5) {
            changeList.addAll(skillType5(config, patronsData, data.getMonsterList().get(param)));
        }
        addMonsterManual(data);
    }

    // ===================================================== 门客 ==============================================================

    /**
     * 刷新出战门客属性
     */
    public void refreshBattlePatrons(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data) {
        // 获取最新数据
        Map<Integer, SeacraftPirateRiotBattlePatrons> newPatronsMap = new ConcurrentHashMap<>();
        for (int i = 1; i <= 5; i++) {
            newPatronsMap.put(i, createBattlePatrons(config, data.getEquipLevel(), i));
        }

        if (data.getChapterStatus() != ePirateRiotChapterStatus.IN_PROGRESS.getValue()) {
            // 关卡外：覆盖
            data.setBattlePatronsMap(newPatronsMap);
        } else {
            // 关卡内：补差值
            for (Map.Entry<Integer, SeacraftPirateRiotBattlePatrons> entry : data.getBattlePatronsMap().entrySet()) {
                SeacraftPirateRiotBattlePatrons newPatrons = newPatronsMap.get(entry.getKey());
                SeacraftPirateRiotBattlePatrons oldPatrons = entry.getValue();

                // 血量补差
                if (oldPatrons.getHpMax() != newPatrons.getHpMax() && oldPatrons.getHp() > 0) {
                    long sub = newPatrons.getHpMax() - oldPatrons.getHpMax();
                    oldPatrons.setHp(oldPatrons.getHp() + sub);
                }

                // 设置最大值
                oldPatrons.setId(newPatrons.getId());
                oldPatrons.setHpMax(newPatrons.getHpMax());
                oldPatrons.setMpMax(newPatrons.getMpMax());
                oldPatrons.setAtk(newPatrons.getAtk());
            }
        }

        data.setUpdateOption();
    }

    /**
     * 生成出战门客
     */
    private SeacraftPirateRiotBattlePatrons createBattlePatrons(SeacraftPirateRiotConfig config, int equipLevel, int occupation) {
        // 取门客数据
        UserPatrons maxPatrons = null; // 注意：如果没有对应职业门客，这个为null
        long totalLevel = 0L;
        long totalQuality = 0L;
        for (UserPatrons tmpPatrons : player.getModule(PatronsModule.class).getUserPatronsList()) {
            if (PatronsMgr.getPatronsInfo(tmpPatrons.getPatronsId()).getOccupation() == occupation) {
                // 计算总值
                totalLevel += tmpPatrons.getLevel();
                totalQuality += tmpPatrons.getTotalQualification();
                // 取最强门客
                if (maxPatrons == null || tmpPatrons.getAbility().compareTo(maxPatrons.getAbility()) > 0) {
                    maxPatrons = tmpPatrons;
                }
            }
        }

        // （基础攻击 + 天赋加成固定值）
        // *（1+战备加成千分比/1000）
        // *（1+临时技能buff千分比/1000）

        // 算攻击力
        List<Integer> attackParam = config.getPatronsAttackParam();
        BigDecimal atkParam1 = BigDecimal.valueOf(attackParam.get(0)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_DOWN);
        BigDecimal atkParam2 = BigDecimal.valueOf(attackParam.get(1)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_DOWN);
        BigDecimal atkParam3 = BigDecimal.valueOf(attackParam.get(2)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_DOWN);
        BigDecimal baseAttack = atkParam1
                .multiply(BigDecimal.valueOf(Math.pow(totalLevel, atkParam2.doubleValue())))
                .add(atkParam3);

        // 基础攻击力 + 天赋加成
        int atkSkillAddition = player.getModule(PatronsModule.class).getSkillAddition(eSkillType.SeacraftPirateRiotAtkAddi.getValue(), 0);
        baseAttack = baseAttack.add(BigDecimal.valueOf(atkSkillAddition));
        baseAttack = baseAttack.add(BigDecimal.valueOf(CurioMgr.getSkillAdditionBySkillType(eSkillType.SeacraftAttackSkill.getValue(), player, 0)));

        // 血量
        List<Integer> hpParam = config.getPatronsHpParam();
        BigDecimal hpParam1 = BigDecimal.valueOf(hpParam.get(0)).divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_DOWN);
        BigDecimal hpParam2 = BigDecimal.valueOf(totalLevel);
        BigDecimal maxHp = hpParam1.multiply(hpParam2);

        // 蓝量
        long maxMp = config.getPatronsMaxMp();
        long initMp = config.getPatronsInitMp();

        // 添加战备加成
        SeacraftPirateRiotEquipConfig equipConfig = config.getEquipConfigMap().get(equipLevel);
        if (equipConfig != null) {
            baseAttack = baseAttack
                    .multiply(BigDecimal.valueOf(1000 + equipConfig.getAttack()))
                    .divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_DOWN);
            maxHp = maxHp
                    .multiply(BigDecimal.valueOf(1000 + equipConfig.getHp()))
                    .divide(BigDecimal.valueOf(1000), 4, BigDecimal.ROUND_DOWN);
        }

        return SeacraftPirateRiotBattlePatrons.create(
                maxPatrons == null ? 0 : maxPatrons.getPatronsId(),
                maxHp.setScale(0, BigDecimal.ROUND_DOWN).longValue(),
                maxMp,
                baseAttack.setScale(0, BigDecimal.ROUND_DOWN).longValue(),
                initMp
        );
    }

    /**
     * 获取随机门客，优先存活
     *
     * @param guide 是否在引导中
     */
    private SeacraftPirateRiotBattlePatrons getRandomPatrons(Map<Integer, SeacraftPirateRiotBattlePatrons> battlePatronsMap, boolean guide) {
        List<SeacraftPirateRiotBattlePatrons> alive = new ArrayList<>();
        List<SeacraftPirateRiotBattlePatrons> dead = new ArrayList<>();
        for (Map.Entry<Integer, SeacraftPirateRiotBattlePatrons> entry : battlePatronsMap.entrySet()) {

            // 新手引导时不会攻击商门客
            int occupation = entry.getKey();
            if (guide && occupation == 4) {
                continue;
            }

            // 正常处理
            SeacraftPirateRiotBattlePatrons data = entry.getValue();
            if (data.getHp() <= 0) {
                // dead.add(data); // 无法攻击死亡门客
            } else {
                alive.add(data);
            }
        }
        if (alive.size() > 0) {
            return alive.get(new Random().nextInt(alive.size()));
        } else if (dead.size() > 0) {
            return dead.get(new Random().nextInt(dead.size()));
        } else {
            return null;
        }
    }

    /**
     * 门客变化
     */
    private void applyPatronsChange(SeacraftPirateRiotBattlePatrons data, SeacraftPirateRiotChange change) {
        // 血量
        if (change.getAddHp() != null) {
            long newHp = data.getHp() + change.getAddHp();
            if (newHp > data.getHpMax()) {
                newHp = data.getHpMax();
            }
            if (newHp < 0) {
                newHp = 0;
            }
            data.setHp(newHp);
        }
        // 蓝量
        if (change.getAddMp() != null) {
            long newMp = data.getMp() + change.getAddMp();
            if (newMp > data.getMpMax()) {
                newMp = data.getMpMax();
            }
            if (newMp < 0) {
                newMp = 0;
            }
            data.setMp(newMp);
        }
    }


    // ===================================================== 怪物 ==============================================================

    /**
     * 怪物是否全部击杀
     */
    private boolean monsterIsAllDead(SeacraftPirateRiotUserData data) {
        for (SeacraftPirateRiotMonsterData monsterData : data.getMonsterList()) {
            if (monsterData.getHp() > 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * 怪物变化
     */
    private void applyMonsterChange(SeacraftPirateRiotMonsterData data, SeacraftPirateRiotChange change) {
        // 血量
        if (change.getAddHp() != null) {
            long newHp = data.getHp() + change.getAddHp();
            if (newHp > data.getHpMax()) {
                newHp = data.getHpMax();
            }
            if (newHp < 0) {
                newHp = 0;
            }
            data.setHp(newHp);
        }
        // 蓝量
        if (change.getAddMp() != null) {
            long newMp = data.getMp() + change.getAddMp();
            if (newMp > data.getMpMax()) {
                newMp = data.getMpMax();
            }
            if (newMp < 0) {
                newMp = 0;
            }
            data.setMp(newMp);
        }
    }

    /**
     * 处理怪物攻击
     */
    private List<SeacraftPirateRiotProto.SeacraftPirateRiotMonsterAttackMsg> handleMonsterAttack(SeacraftPirateRiotConfig config,
                                                                                                 SeacraftPirateRiotUserData data,
                                                                                                 Map<Integer, SeacraftPirateRiotBattlePatrons> occupationBattleMap) {

        List<SeacraftPirateRiotProto.SeacraftPirateRiotMonsterAttackMsg> list = new ArrayList<>();
        List<SeacraftPirateRiotMonsterData> monsterList = data.getMonsterList();

        // 获取可以攻击、放技能的怪物
        List<SeacraftPirateRiotMonsterData> atkMonsterList = new ArrayList<>();
        List<SeacraftPirateRiotMonsterData> skillMonsterList = new ArrayList<>();
        for (SeacraftPirateRiotMonsterData monsterData : monsterList) {
            if (monsterData.getHp() <= 0) {
                continue; // 跳过阵亡的
            }
            // 到达攻击回合才可以：攻击或放技能
            if (monsterData.getAtkCdMax() > 0 && monsterData.getAtkCd() <= 1) {
                if (monsterData.getMpMax() > 0 && monsterData.getMp() >= monsterData.getMpMax()) {
                    skillMonsterList.add(monsterData);
                } else {
                    atkMonsterList.add(monsterData);
                }
            }
        }

        // 按攻击顺序排序
        atkMonsterList.sort(new SeacraftPirateRiotMonsterAttackSort(config));
        skillMonsterList.sort(new SeacraftPirateRiotMonsterAttackSort(config));

        // 【普通攻击】
        for (SeacraftPirateRiotMonsterData monsterData : atkMonsterList) {
            SeacraftPirateRiotBattlePatrons randomPatrons = getRandomPatrons(occupationBattleMap, data.getGuideIndex() < config.getInitBlock().length);
            if (randomPatrons != null) {
                // 攻击
                SeacraftPirateRiotChange change = new SeacraftPirateRiotChange(randomPatrons.getId(), -1 * calcBuffAtk(monsterData.getAtk(), monsterData.getBuffList()), null, null);
                applyPatronsChange(randomPatrons, change);
                // 记录结果
                list.add(SeacraftPirateRiotPb.parseSeacraftPirateRiotMonsterAttackMsg(monsterData.getIndex(), 1, Collections.singletonList(change)).build());
            }
            // 重置CD
            monsterData.setAtkCd(monsterData.getAtkCdMax() + 1); // 多设置1回合，等下回合结束后才是MAX
        }

        // 【技能攻击】
        for (SeacraftPirateRiotMonsterData monsterData : skillMonsterList) {
            List<SeacraftPirateRiotChange> changeList = new ArrayList<>();
            for (SeacraftPirateRiotBattlePatrons patronsData : occupationBattleMap.values()) {
                // 无法攻击死亡门客
                if (patronsData.getHp() <= 0) {
                    continue;
                }
                // 攻击
                SeacraftPirateRiotChange change = new SeacraftPirateRiotChange(patronsData.getId(), -1 * calcBuffAtk(monsterData.getAtk(), monsterData.getBuffList()), null, null);
                applyPatronsChange(patronsData, change);
                changeList.add(change);
            }
            // 重置CD和蓝量
            monsterData.setAtkCd(monsterData.getAtkCdMax() + 1); // 多设置1回合，等下回合结束后才是MAX
            monsterData.setMp(0);
            // 记录结果
            list.add(SeacraftPirateRiotPb.parseSeacraftPirateRiotMonsterAttackMsg(monsterData.getIndex(), 2, changeList).build());
        }

        return list;
    }


    // ===================================================== 消除 ==============================================================

    /**
     * 计算方块攻击的怪物下标
     *
     * @return -1没打到
     */
    private int getHitIndex(List<SeacraftPirateRiotMonsterData> monsterDataList, SeacraftPirateRiotBlock block) {
        int minHeight = Integer.MAX_VALUE;
        int index = -1;

        for (SeacraftPirateRiotMonsterData monsterData : monsterDataList) {
            // 存活 && 受击范围内
            if (monsterData.getHp() > 0 && block.getX() + 1 >= monsterData.getL() && block.getX() + 1 <= monsterData.getR()) {
                // 最低高度的先打到
                if (monsterData.getH() < minHeight) {
                    minHeight = monsterData.getH();
                    index = monsterData.getIndex();
                }
            }
        }

        return index;
    }

    /**
     * 按攻击顺序排序
     */
    private List<List<SeacraftPirateRiotBlock>> sortByHitOrder(List<SeacraftPirateRiotBlock> blocks) {
        // Y大到小排序
        List<SeacraftPirateRiotBlock> sortBlocks = new ArrayList<>(blocks);
        Collections.sort(sortBlocks, Comparator.comparing(SeacraftPirateRiotBlock::getY).reversed());

        // 按Y值相同分成多个LIST
        List<List<SeacraftPirateRiotBlock>> listList = new ArrayList<>();
        int curY = Integer.MAX_VALUE;
        for (SeacraftPirateRiotBlock block : sortBlocks) {
            if (block.getY() != curY) {
                listList.add(new ArrayList<>());
                curY = block.getY();
            }
            listList.get(listList.size() - 1).add(block);
        }

        // 按X到中间点距离排序
        for (List<SeacraftPirateRiotBlock> list : listList) {
            Collections.sort(list, new Comparator<SeacraftPirateRiotBlock>() {
                @Override
                public int compare(SeacraftPirateRiotBlock o1, SeacraftPirateRiotBlock o2) {
                    // 中间点下标=(宽度-1)/2，这边不用小数就全部乘2了
                    int middle = SeacraftPirateRiotEliminateLogic.MAX_WIDTH - 1;
                    int sub = Math.abs(2 * o1.getX() - middle) - Math.abs(2 * o2.getX() - middle);
                    if (sub != 0) {
                        return sub > 0 ? 1 : -1;
                    }
                    // 一样大就左边先
                    return o1.getX() <= o2.getX() ? 1 : -1;
                }
            }.reversed()); // 反过来大的才在前面
        }
        return listList;
    }

    /**
     * 计算方块伤害
     */
    private long calcBlockDamage(SeacraftPirateRiotConfig config, long patronsAtk, int continueNum, boolean alive) {
        // 基础伤害 = 配置系数 * 门客当前攻击
        long baseDamage = config.getBlockDamage() * patronsAtk / 1000;

        // 最终伤害 = 基础伤害 * 连消倍率系数
        List<Integer> addiList = config.getBlockDamageAdditionList();
        int blockAddi = continueNum > addiList.size() ? addiList.get(addiList.size() - 1) : addiList.get(continueNum - 1);
        long blockDamage = baseDamage * blockAddi / 1000;

        // 阵亡也有保底伤害
        if (!alive) {
            blockDamage = blockDamage * config.getDeadDamagePercent() / 1000;
        }

        return blockDamage < 0 ? 0 : blockDamage;
    }

    /**
     * 处理消除结果1/3
     */
    private List<SeacraftPirateRiotProto.SeacraftPirateRiotEliminateRoundMsg> handleEliminateResult(SeacraftPirateRiotEliminateResult eliminateResult,
                                                                                                    SeacraftPirateRiotConfig config,
                                                                                                    SeacraftPirateRiotUserData data,
                                                                                                    Map<Integer, SeacraftPirateRiotBattlePatrons> occupationBattleMap,
                                                                                                    List<String> logMonster) {
        List<SeacraftPirateRiotProto.SeacraftPirateRiotEliminateRoundMsg> list = new ArrayList<>();

        // 简单校验
        if (eliminateResult.getRet() != 0 || eliminateResult.getRoundResultList().size() <= 0) {
            return list;
        }

        // 处理每轮结果
        int continueNum = 0;
        List<SeacraftPirateRiotEliminateRoundResult> roundResultList = eliminateResult.getRoundResultList();
        for (SeacraftPirateRiotEliminateRoundResult roundResult : roundResultList) {
            continueNum++;
            list.add(handleEliminateRoundResult(roundResult, config, data, occupationBattleMap, continueNum).build());

            // 日志
            if (continueNum == 1) {
                logMonster.set(1, SeacraftPirateRiotLogHelper.parseMonsterCurHp(data.getMonsterList()));
            }
        }

        // 替换盘面、引导下标
        SeacraftPirateRiotEliminateRoundResult lastRound = roundResultList.get(roundResultList.size() - 1);
        data.setBlockList(lastRound.getBlockList());
        data.setGuideIndex(eliminateResult.getGuideIndex());

        return list;
    }

    /**
     * 处理消除结果2/3：每轮的结果
     */
    private SeacraftPirateRiotProto.SeacraftPirateRiotEliminateRoundMsg.Builder handleEliminateRoundResult(SeacraftPirateRiotEliminateRoundResult roundResult,
                                                                                                           SeacraftPirateRiotConfig config,
                                                                                                           SeacraftPirateRiotUserData data,
                                                                                                           Map<Integer, SeacraftPirateRiotBattlePatrons> occupationBattleMap,
                                                                                                           int continueNum) {
        SeacraftPirateRiotProto.SeacraftPirateRiotEliminateRoundMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotEliminateRoundMsg.newBuilder();
        builder.setType(roundResult.getType());
        for (SeacraftPirateRiotBlock bomb : roundResult.getCreateBomb()) {
            builder.addCreateBombList(SeacraftPirateRiotPb.parseSeacraftPirateBlockMsg(bomb));
        }
        builder.addAllBlockMsgList(SeacraftPirateRiotPb.parseSeacraftPirateBlockMsgList(roundResult.getBlockList()));

        // 处理阶段结果
        for (SeacraftPirateRiotEliminateStageResult stageResult : roundResult.getStageResultList()) {
            builder.addStageList(handleEliminateStageResult(stageResult, config, data, occupationBattleMap, continueNum));
        }

        return builder;
    }

    /**
     * 处理消除结果3/3：每阶段的结果
     */
    private SeacraftPirateRiotProto.SeacraftPirateRiotEliminateStageMsg.Builder handleEliminateStageResult(SeacraftPirateRiotEliminateStageResult stageResult,
                                                                                                           SeacraftPirateRiotConfig config,
                                                                                                           SeacraftPirateRiotUserData data,
                                                                                                           Map<Integer, SeacraftPirateRiotBattlePatrons> occupationBattleMap,
                                                                                                           int continueNum) {
        List<List<SeacraftPirateRiotBlock>> sortListList = sortByHitOrder(stageResult.getBlockList());

        // 【处理消除方块效果】
        for (List<SeacraftPirateRiotBlock> sortList : sortListList) {
            for (SeacraftPirateRiotBlock block : sortList) {

                SeacraftPirateRiotBattlePatrons patronsData = occupationBattleMap.get(block.getC());

                // 跳过炸弹
                if (patronsData == null) {
                    continue;
                }

                // 找打到的怪
                int hitIndex = getHitIndex(data.getMonsterList(), block);
                if (hitIndex == -1) {
                    continue;
                }

                boolean patronsAlive = patronsData.getHp() > 0;

                // 打怪物
                long patronsDamage = calcBlockDamage(config, calcBuffAtk(patronsData.getAtk(), patronsData.getBuffList()), continueNum, patronsAlive);
                SeacraftPirateRiotChange monsterChange = new SeacraftPirateRiotChange(hitIndex, -1 * patronsDamage, patronsDamage, null);
                applyMonsterChange(data.getMonsterList().get(hitIndex), monsterChange);
                block.getChangeList().add(monsterChange);

                // 攒自身蓝量
                if (patronsAlive) {
                    SeacraftPirateRiotChange patronsChange = new SeacraftPirateRiotChange(occupationBattleMap.get(block.getC()).getId(), null, Long.valueOf(config.getBlockMp()), null);
                    applyPatronsChange(occupationBattleMap.get(block.getC()), patronsChange);
                    block.getChangeList().add(patronsChange);
                }

            }
        }

        // 【组装返回值】
        SeacraftPirateRiotProto.SeacraftPirateRiotEliminateStageMsg.Builder builder = SeacraftPirateRiotProto.SeacraftPirateRiotEliminateStageMsg.newBuilder();
        for (SeacraftPirateRiotBlock block : stageResult.getBlockList()) {
            builder.addBlockList(SeacraftPirateRiotPb.parseSeacraftPirateBlockMsg(block));
        }
        for (SeacraftPirateRiotBoom boom : stageResult.getBoomList()) {
            builder.addBoomList(SeacraftPirateRiotPb.parseSeacraftPirateRiotBoomMsg(boom));
        }
        return builder;
    }


    // ===================================================== 其他 ==============================================================

    /**
     * 扣回合数
     */
    private void roundReduce(SeacraftPirateRiotUserData data) {
        // 门客
        Map<Integer, SeacraftPirateRiotBattlePatrons> battlePatronsMap = data.getBattlePatronsMap();
        for (SeacraftPirateRiotBattlePatrons battlePatrons : battlePatronsMap.values()) {
            buffRoundReduce(battlePatrons.getBuffList()); // BUFF减回合
        }
        // 怪物
        for (SeacraftPirateRiotMonsterData monsterData : data.getMonsterList()) {
            buffRoundReduce(monsterData.getBuffList()); // BUFF减回合
            if (monsterData.getAtkCd() > 0) {
                monsterData.setAtkCd(monsterData.getAtkCd() - 1); // 攻击间隔减回合
            }
        }
    }

    /**
     * buff扣回合数
     */
    private void buffRoundReduce(List<SeacraftPirateRiotBuff> buffList) {
        for (int i = 0; i < buffList.size(); i++) {
            SeacraftPirateRiotBuff buff = buffList.get(i);
            buff.setRound(buff.getRound() - 1);
            if (buff.getRound() <= 0) {
                buffList.remove(i);
                i--;
            }
        }
    }

    /**
     * 加排行积分
     */
    private void addRankScore(SeacraftPirateRiotConfig config, SeacraftPirateRiotUserData data, String reward) {
        Property property = PropertyHelper.parseStringToProperty(reward);
        BigInteger count = property.getCountByGoodsId(config.getScoreItemId());
        data.setScore(data.getScore() + count.longValue());
        player.notifyListener(eGamePlayerEventType.SeacraftPirateRiotScoreRank.getValue(), new SeacraftPirateRiotScoreRankArgs(data.getScore(), SeacraftPirateRiotMgr.getRankChapterId(data)));
        player.notifyListener(eGamePlayerEventType.SeacraftPirateRiotUnionScoreRank.getValue(),
                new UnionRiseRankArgs(UnionRiseRankArgs.NOTIFY_TYPE_NORMAL, BigInteger.valueOf(data.getScore()), player.getUserInfo().getUnionUid()));
    }

    /**
     * 扣体力
     */
    private boolean removeEnergy(SeacraftPirateRiotConfig config, int count) {
        SeacraftPirateRiotUserData data = getUserData(); // 取数据，顺便计算下体力恢复
        if (data == null || data.getEnergy() < count) {
            return false;
        }

        boolean beforeMax = data.getEnergy() >= config.getMaxEnergy();
        data.setEnergy(data.getEnergy() - count); // 扣体力

        if (beforeMax && data.getEnergy() < config.getMaxEnergy()) {
            data.setLastRecTime(System.currentTimeMillis()); // 从满扣到不满，修改下恢复时间
        }
        return true;
    }

    /**
     * 计算BUFF后攻击
     */
    private long calcBuffAtk(long baseAtk, List<SeacraftPirateRiotBuff> buffList) {
        long atkAddi = 0;
        for (SeacraftPirateRiotBuff buff : buffList) {
            // 跳过结束的BUFF
            if (buff.getRound() <= 0) {
                continue;
            }
            // 攻击力变化的BUFF
            if (buff.getType() == 1 || buff.getType() == 3) {
                atkAddi += buff.getParam();
            }
        }
        long atk = baseAtk + atkAddi;
        return atk < 0 ? 0 : atk;
    }

    private void addMonsterManual(SeacraftPirateRiotUserData data) {
        for (SeacraftPirateRiotMonsterData monsterData : data.getMonsterList()) {
            if (monsterData.getHp() <= 0 && !data.getMonsterManual().contains(monsterData.getId())) {
                data.getMonsterManual().add(monsterData.getId());
            }
        }
    }

}
